import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';

import 'package:flustars_flutter3/flustars_flutter3.dart';
import 'package:flutter_deer/database/beans/get_mqtt_info_entity.dart';
import 'package:flutter_deer/database/beans/get_user_info_entity.dart';
import 'package:flutter_deer/database/beans/mqtt_msg_model.dart';
import 'package:flutter_deer/res/constant.dart';
import 'package:flutter_deer/util/toast_utils.dart';
import 'package:flutter_deer/util/uuid_util.dart';
import 'package:mqtt_client/mqtt_client.dart';
import 'package:mqtt_client/mqtt_server_client.dart';

import 'date_utils.dart';

class MqttManager {

  factory MqttManager() {
    return _instance;
  }

  MqttManager._internal();

  static final MqttManager _instance = MqttManager._internal();

  late MqttClient client;
  var pongCount = 0;
  var pingCount = 0;

  MqttCallback? callback;

  late GetMqttInfoEntity? mqttBean;

  // init
  Future<void> initMqtt({required MqttCallback callback}) async {
    this.callback = callback;

    mqttBean = GetMqttInfoEntity.get();
    if (mqttBean == null) {
      Toast.show('mqttInfo is null');
      return;
    }

    // 连接到 MQTT 服务器 MqttBrowserClient
    configureClient();
    // 连接到服务器
    await handleConnection();

    // 检查连接状态
    if (client.connectionStatus!.state == MqttConnectionState.connected) {
      print('[mqtt]------>>> EXAMPLE::[Connected]');
    } else {
      print(
        '[mqtt]------>>> EXAMPLE::[ERROR] client connection failed - disconnecting, status is ${client.connectionStatus}',
      );
      client.disconnect();
      print('[mqtt]------>>> disconnect');
      // exit(-1);
    }
    // 订阅主题
    print('[mqtt]------>>> [订阅主题] to the test/mqtt topic');
    mqttBean?.mqttInfo?.subscribes?.forEach((element) {
      client.subscribe(element, MqttQos.atLeastOnce);
    });
    // client.subscribe('test/mqtt', MqttQos.atLeastOnce);
    // 监听订阅主题更新
    client.updates!.listen((List<MqttReceivedMessage<MqttMessage?>>? c) {
      final recMess = c![0].payload! as MqttPublishMessage;
      final pt = MqttPublishPayload.bytesToStringAsString(recMess.payload.message);
      // final topic = c[0].topic;
      // print('[mqtt-push]------');
      parseData(pt);
    });
    // 监听已发布消息
    client.published!.listen((MqttPublishMessage message) {
      // print('[mqtt-push]------>>> [监听已发布消息]:: topic is ${message.variableHeader!.topicName}, with Qos ${message.header!.qos}');
    });
    // 发布消息
    final builder = MqttClientPayloadBuilder();
    builder.addString('Hello from MQTT');
    // print('[mqtt-push]------>>> [发布消息]');
    client.publishMessage('test/mqtt', MqttQos.atLeastOnce, builder.payload!);

    // 休眠一段时间，观察保持连接机制
    // await MqttUtilities.asyncSleep(60);

    // 打印保持连接的延迟数据
    // print('------>>> EXAMPLE::Keep alive latencies');
    // print('------>>> The latency of the last ping/pong cycle is ${client.lastCycleLatency} milliseconds',);
    // print('------>>> The average latency of all the ping/pong cycles is ${client.averageCycleLatency} milliseconds',);

    // 取消订阅并断开连接
    // client.unsubscribe(topic);
    // await MqttUtilities.asyncSleep(2);
    // client.disconnect();
    // print('EXAMPLE::Exiting normally');
  }

  void configureClient() {
    client = MqttServerClient(mqttBean?.mqttInfo?.host ?? '', GetUserInfoEntity.getDeviceId());
    client.logging(on: false);
    client.setProtocolV311();
    client.keepAlivePeriod = 20;
    client.autoReconnect = true;
    client.connectTimeoutPeriod = 30000;
    // 设置回调函数
    client.onDisconnected = onDisconnected;
    client.onConnected = onConnected;
    client.onAutoReconnected = onConnected;
    client.onSubscribed = onSubscribed;
    // client.pongCallback = pong;
    // client.pingCallback = ping;
    // 创建连接消息
    final connMess = MqttConnectMessage()
        .withClientIdentifier(client.clientIdentifier)
        .withWillTopic('willtopic') // If you set this you must set a will message
        .withWillMessage('My Will message')
        //.startClean() // Non persistent session for testing
        .withWillQos(MqttQos.atLeastOnce)
        .authenticateAs(mqttBean?.username ?? '', mqttBean?.password ?? '');
    client.connectionMessage = connMess;
  }

  Future<void> handleConnection() async {
    try {
      print('[mqtt]------>>> EXAMPLE::[Connecting]....');
      await client.connect();
    } on NoConnectionException catch (e) {
      print('[mqtt]------>>> EXAMPLE::client exception - $e');
      client.disconnect();
    } on SocketException catch (e) {
      print('[mqtt]------>>> EXAMPLE::Socket exception - $e');
      client.disconnect();
    } on Exception catch (e) {
      print('[mqtt]------>>> EXAMPLE::Unexpected exception - $e.');
      client.disconnect();
    }
  }

  // 订阅确认回调
  void onSubscribed(String topic) {
    // print('[mqtt-push]------>>> EXAMPLE::[Subscription confirmed] for topic $topic');
  }

  // 对外方法，连接
  Future<void> connect() async {
    // 连接到服务器
    try {
      print('[mqtt-push]------>>> EXAMPLE::[Connecting]....');
      await client.connect();
    } on NoConnectionException catch (e) {
      print('[mqtt-push]------>>> EXAMPLE::client exception - $e');
      client.disconnect();
    } on SocketException catch (e) {
      print('[mqtt-push]------>>> EXAMPLE::Socket exception - $e');
      client.disconnect();
    } on Exception catch (e) {
      print('[mqtt-push]------>>> EXAMPLE::Unexpected exception - $e.');
      client.disconnect();
    }
  }

  // 对外方法，断开连接
  Future<void> disconnect() async {
    client.disconnect();
  }

  // 对外方法，发送消息
  Future<void> sendMessage(String message) async {
    if (client.connectionStatus!.state != MqttConnectionState.connected) {
      // Toast.show('mqtt not connected');
      return;
    }
    final builder = MqttClientPayloadBuilder();
    builder.addString(message);
    // print('------>>> [发送消息]:: topic=[$topic], message=[$message]');
    client.publishMessage(mqttBean?.mqttInfo?.receipt ?? '', MqttQos.atLeastOnce, builder.payload!);
  }

  // 断开连接回调
  void onDisconnected() {
    callback?.onConnect(false);
    print('[mqtt-push]------>>> EXAMPLE::[OnDisconnected] client callback - Client disconnection');
    if (client.connectionStatus!.disconnectionOrigin == MqttDisconnectionOrigin.solicited) {
      // print('[mqtt-push]------>>> EXAMPLE::[OnDisconnected] callback is solicited, this is correct');
    } else {
      // print('[mqtt-push]------>>> EXAMPLE::[OnDisconnected] callback is unsolicited or none, this is incorrect - exiting');
      // exit(-1);
    }
    if (pongCount == 3) {
      // print('[mqtt-push]EXAMPLE:: Pong count is correct');
    } else {
      // print('[mqtt-push]EXAMPLE:: Pong count is incorrect, expected 3. actual $pongCount');
    }
  }

  // 连接成功回调
  void onConnected() {
    callback?.onConnect(true);
    print('[mqtt-push]------>>> EXAMPLE::OnConnected successful');
  }

  // 收到 Pong 消息回调
  void pong() {
    // print('[mqtt-push]------>>> EXAMPLE::Ping ');
    pongCount++;
    // print(
      // '[mqtt-push]------>>> EXAMPLE::Latency of this ping/pong cycle is ${client.lastCycleLatency} milliseconds',
    // );
  }

  // 发送 Ping 消息回调
  void ping() {
    pingCount++;
  }

  // 解析数据
  void parseData(String content, {String tag = 'push'}) {
    // print('------>>> [原数据] [$content]');
    // 解析数据 header body
    final MsgString? msgString = MsgString.toMsgString(content);
    final decodeHeaderMsg = decodeHeader(msgString?.header);
    // Log.json(decodeHeaderMsg!, customTag: '------>>> [Mqtt-header]');

    // 解析header
    final headerMap = jsonDecode(decodeHeaderMsg);
    final String encode = headerMap['encode'].toString();
    final String subTypeHeader = headerMap['subType'].toString();
    final String idHeader = headerMap['id'].toString();
    // print('------>>> [Mqtt-encode] [$encode] [subTypeHeader] [$subTypeHeader] [idHeader] [$idHeader]');

    // 解析body
    final bodyString = decodeBody(msgString?.body, encode);
    // Log.json(bodyString, customTag: '------>>> [Mqtt-body]');

    // 组装数据
    final bodyJson = json.decode(bodyString) as Map<String, dynamic>;
    final String msgId = bodyJson['id']?.toString() ?? '';
    final String msgContent = bodyJson['data']?.toString() ?? '';
    final String subType = bodyJson['subType']?.toString() ?? '';
    final String type = bodyJson['type']?.toString() ?? '';

    final MqttMsgModel mqttMsgModel = MqttMsgModel()
      ..msgId = msgId
      ..msgContent = msgContent
      ..subType = subType
      ..type = type
      ..localTimestamp = DateUtils.fullDayFormat(DateTime.now());

    callback?.onMessageReceived(mqttMsgModel, tag);

    // 组装回复消息
    _replyMsg(subTypeHeader, idHeader);
  }

  // 组装一个Json MsgHeader{id=-8478821678848063364, type='RECEIPT', subType='null', encode='GZIP', timestamp=null}
  void _replyMsg(String subTypeHeader, String idHeader) {
    final Map<String, dynamic> header = {
      'id': UUIDUtils.getRandomUUIDMostSignificantBits(),
      // 'id': 1674018530864809042,
      'type': 'RECEIPT',
      'encode': 'GZIP',
      'subType': 'null',
      'timestamp': null,
    };
    final String headerString = jsonEncode(header);

    final Map<String, dynamic> body = {
      'msgId': idHeader,
      'sn': SpUtil.getString(Constant.snCode),
      'subtopic': subTypeHeader,
      // 'subtopic': topic,
      'userId': GetUserInfoEntity.get()?.id ?? '',
    };
    final String bodyString = jsonEncode(body);
    final encoder = '${encodeHeader(headerString)}.${encodeBody(bodyString)}';
    // print('------>>> [回复] [$encoder] ');
    // print('------>>> [回复消息] [$headerString] [$bodyString]');

    // 回复消息
    sendMessage(encoder);
  }

  // 解码 Base64 字符串 header
  static String decodeHeader(String? header) {
    if (header == null) {
      return '';
    }

    try {
      // 修复 Base64 字符串长度
      String normalizedHeader = header;
      while (normalizedHeader.length % 4 != 0) {
        normalizedHeader += '=';
      }

      // 解码 Base64
      final Uint8List bytes = base64Url.decode(normalizedHeader);
      // 转换为 UTF-8 字符串
      return utf8.decode(bytes);
    } catch (e) {
      print('------>>> [Base64 解码失败]: $e');
      return '';
    }
  }

  // 解码 Base64 字符串 body
  static String decodeBody(String? body, String? encode) {
    if (body == null) {
      return '';
    }
    try {
      // 修复 Base64 字符串长度
      String normalizedBody = body;
      while (normalizedBody.length % 4 != 0) {
        normalizedBody += '=';
      }
      // 解码 Base64
      final Uint8List bodyBytes = base64Url.decode(normalizedBody);

      // 检查是否为 GZIP 压缩数据
      if (encode == 'GZIP') {
        if (bodyBytes.length >= 2 && bodyBytes[0] == 0x1F && bodyBytes[1] == 0x8B) {
          // 解压
          final body = gzip.decode(bodyBytes as List<int>);
          final bodyString = utf8.decode(body);
          return bodyString;
        } else {
          print('[mqtt]------>>> EXAMPLE::数据不是 GZIP 压缩格式');
          return '';
        }
      }

      // 转换为 UTF-8 字符串
      return utf8.decode(bodyBytes);
    } catch (e) {
      print('[mqtt]------>>> [Base64 decodeBody 解码失败]: $e');
      return '';
    }
  }

  // 加码 Base64 字符串 header
  static String encodeHeader(String header) {
    final Uint8List bytes = utf8.encode(header);
    return base64Url.encode(bytes);
  }

  // 加码 Base64 字符串 body 先Gzip压缩 后Base64
  static String encodeBody(String body) {
    // 1. Gzip压缩
    final List<int> bytes = gzip.encode(utf8.encode(body));
    // 2. Base64编码
    return base64Url.encode(bytes);
  }

  // 获取连接状态
  bool get isConnected => client.connectionStatus?.state == MqttConnectionState.connected;

}

class MsgString {
  MsgString(this.header, this.body);

  final String? header;
  final String body;

  static MsgString? toMsgString(String? content) {
    // 1. 空值检查
    if (content == null || content.isEmpty) {
      return MsgString(null, '');
    }

    // 2. 查找点号位置
    int index = content.indexOf(".");

    // 3. 分割字符串
    String? header;
    String body;

    if (index > 0) {
      header = content.substring(0, index);
      body = content.substring(index + 1);
    } else {
      header = null;
      body = content;
    }

    // 4. 返回 MsgString 对象
    return MsgString(header, body);
  }
}

/*
header
{
   encode: "GZIP",
   id: "1000200000093863490",
   subType: "WORK_CLASS_KNOT_TICKET_CONFIG",
   timestamp: "1751013403853",
   type: "RECEIPT_SETTING_NOTICE"
}
{
   data: "{"configKey":"WORK_CLASS_KNOT_TICKET_CONFIG"}",
   duration: "10",
   id: "1000200000000414842",
   mchCode: "NY101171184177",
   subType: "WORK_CLASS_KNOT_TICKET_CONFIG",
   type: "RECEIPT_SETTING_NOTICE"
}
*/

abstract class MqttCallback {
  void onConnect(bool isSuccess);

  void onMessageReceived(MqttMsgModel message, String tag);
}
